package com.gsi.webstore.platform.storefront.internal.basket;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import com.gsi.webstore.platform.storefront.capi.basket.GSIBasketBO;
import com.gsi.webstore.platform.storefront.capi.order.GSILineItemCtnrCalculationResultView;
import com.gsi.webstore.platform.storefront.capi.order.GSIProductLineItemBO;
import com.gsi.webstore.platform.storefront.capi.shipping.GSIShippingBucketBO;
import com.gsi.webstore.platform.storefront.capi.shipping.GSIShippingMethodSelectionBucketBO;
import com.gsi.webstore.platform.storefront.internal.order.GSIProductLineItemBOImpl;
import com.gsi.webstore.platform.storefront.internal.shipping.GSIShippingBucketBOImpl;
import com.gsi.webstore.platform.storefront.internal.shipping.GSIShippingMethodSelectionBucketBOImpl;
import com.intershop.beehive.bts.capi.orderprocess.LineItemCtnr;
import com.intershop.beehive.core.capi.component.ComponentMgr;
import com.intershop.beehive.core.capi.domain.Domain;
import com.intershop.beehive.core.capi.domain.DomainMgr;
import com.intershop.beehive.core.capi.log.Logger;
import com.intershop.beehive.core.capi.naming.NamingMgr;
import com.intershop.beehive.core.capi.preference.PreferenceMgr;
import com.intershop.beehive.core.capi.preference.PreferenceNotFoundException;
import com.intershop.beehive.core.capi.preference.ValueTypeViolationException;
import com.intershop.beehive.foundation.quantity.CurrencyException;
import com.intershop.beehive.foundation.quantity.Money;
import com.intershop.beehive.foundation.quantity.MoneyCalculator;
import com.intershop.component.shipping.capi.shippingbucket.ShippingBucket;
import com.intershop.component.shipping.capi.shippingbucketcomposer.ShippingBucketComposer;
import com.intershop.component.shipping_data.capi.shippingmethod.ShippingMethod;
import com.intershop.component.shipping_data.capi.shippingmethod.ShippingMethodComparator;
import com.intershop.sellside.appbase.b2c.capi.basket.B2CBasketBO;
import com.intershop.sellside.appbase.b2c.capi.basket.B2CBasketBOContext;
import com.intershop.sellside.appbase.b2c.capi.basket.B2CBasketBOProxy;
import com.intershop.sellside.appbase.b2c.capi.calculation.LineItemCtnrCalculationResultView;
import com.intershop.sellside.appbase.b2c.capi.order.CostInformationBO;
import com.intershop.sellside.appbase.b2c.capi.order.ProductLineItemBO;
import com.intershop.sellside.appbase.b2c.capi.shipping.ShippingBucketBO;
import com.intershop.sellside.appbase.b2c.capi.shipping.ShippingMethodSelectionBucketBO;
import com.intershop.sellside.appbase.b2c.capi.shipping.ShippingMethodSelectionBucketComposer;
import com.intershop.sellside.appbase.b2c.internal.basket.B2CBasketBOImpl;
import com.intershop.sellside.appbase.b2c.internal.order.LineItemCtnrBOImpl;
import com.intershop.sellside.appbase.b2c.internal.shipping.ShippingBucketBOImpl;

/**
 * The GSI specific version of the Basket business object.
 * This class is used to customize the behavior of the original business object.
 *
 * @author Cristina Rodriguez; Norman Kubicek; Nils Breitmann
 *
 */
public class GSIBasketBOImpl extends B2CBasketBOProxy implements GSIBasketBO
{
    private final B2CBasketBOContext context;
    private final B2CBasketBO delegate;
    private final LineItemCtnrCalculationResultView calculationResultView;
    private List<GSIProductLineItemBO> productLineItems;
    private Collection<CostInformationBO> salesTaxes;
    private Collection<CostInformationBO> dutiesAndFees;


    private Boolean hiddenEdd = null;

    public GSIBasketBOImpl(final B2CBasketBO delegate, final B2CBasketBOContext context, final LineItemCtnrCalculationResultView calculationResultView)
    {
        super(delegate);
        this.delegate = delegate;
        this.context = context;
        this.calculationResultView = calculationResultView;
    }

    @Override
    public Collection<? extends ProductLineItemBO> getProductLineItems()
    {
        if (productLineItems != null)
        {
            return productLineItems;
        }

        productLineItems = new ArrayList<GSIProductLineItemBO>();
        for (final ProductLineItemBO pliBO : delegate.getProductLineItems())
        {
            productLineItems.add(createProductLineItemBO(pliBO));
        }
        return productLineItems;
    }

    @Override
    public boolean isHiddenEDD()
    {
        if(hiddenEdd ==null)
        {
          final Domain site=getDelegate().getSite();
          Domain preferenceDomain=null;
          String eddMode=EDD_MODE_ENABLED; //default

          // todo: PreferenceMgr should be part of the context
          if (null != site && null != site.getDomainName())
          {
              final String siteName = site.getDomainName();
              if (siteName.endsWith("-Site"))
              {
                  final DomainMgr domainMgr = NamingMgr.getManager(DomainMgr.class);
                  preferenceDomain = domainMgr.getDomainByName(siteName.substring(0, siteName.length()-5).concat("-Anonymous"));
              }
          }
          if (preferenceDomain!=null)
          {
              final PreferenceMgr pmgr=(PreferenceMgr)NamingMgr.getInstance().lookupManager(PreferenceMgr.REGISTRY_NAME);
              try
              {
                  eddMode=pmgr.getStringPreference(EDD_MODE_CHANEL_PREFERENCE,preferenceDomain);
              }
              catch(PreferenceNotFoundException e)
              {
                  Logger.error(this, "preference " + EDD_MODE_CHANEL_PREFERENCE + " no found ", e);
              }
              catch(ValueTypeViolationException e)
              {
                  Logger.error(this, " value type violation ", e);
              }
          }
          hiddenEdd=Boolean.valueOf(!EDD_MODE_ENABLED.equals(eddMode));
        }
        return hiddenEdd.booleanValue();
    }

    private GSIProductLineItemBO createProductLineItemBO(final ProductLineItemBO pliBO)
    {
        return new GSIProductLineItemBOImpl(pliBO, this, delegate.getCalculationResultView());
    }



    //
    // quick hack to fix V11EXT-2872
    // todo: needs rework
    //

    @Override
    public Collection<? extends ShippingMethodSelectionBucketBO> getShippingMethodSelectionBucketBOs()
    {
        return getShippingMethodSelectionBucketBOsSet();
    }

    @Override
    public Set<? extends ShippingMethodSelectionBucketBO> getShippingMethodSelectionBucketBOsSet()
    {

        final ComponentMgr componentMgr = NamingMgr.getManager(ComponentMgr.class);
        final ShippingMethodSelectionBucketComposer composer = componentMgr.getComponent("shippingMethodSelectionBucketComposer");

        final Set<? extends ShippingMethodSelectionBucketBO> buckets = composer.composeShippingBuckets(this, context);  
        if (buckets != null)
        {
            return buckets;
        }
        return new HashSet<ShippingMethodSelectionBucketBO>();

    }
    // end of quick hack

    @Override
    public Money getSalesTaxesAmount() throws CurrencyException
    {
        return calculationResultView.getTotalTax();
    }

    @Override
    public Money getDutiesAndFeesAmount() throws CurrencyException
    {
        return ((GSILineItemCtnrCalculationResultView)calculationResultView).getTotalDutiesAndFees();
    }

    @Override
    public Money getTaxesDutiesAndFeesAmount() throws CurrencyException
    {
        return MoneyCalculator.createLowPrecisionCalculator().add(getSalesTaxesAmount(), getDutiesAndFeesAmount());
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends CostInformationBO> Collection<T> getSalesTaxes()
    {
        if (salesTaxes == null)
        {
            salesTaxes=((GSILineItemCtnrCalculationResultView)calculationResultView).getTaxGroups();
        }

        return (Collection<T>)salesTaxes;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends CostInformationBO> Collection<T> getDutiesAndFees()
    {
        if (dutiesAndFees == null)
        {
            dutiesAndFees=((GSILineItemCtnrCalculationResultView)calculationResultView).getDutiesAndFeesGroups();
        }
        return (Collection<T>)dutiesAndFees;
    }

    @Override
    public boolean isTaxCalculated()
    {
        return ((GSILineItemCtnrCalculationResultView)calculationResultView).getTaxServiceErrorItem() == null;
    }
    
    
    
    
    
    //
    // quick hack to fix V11EXT-2872
    // todo: needs rework
    //   
    
    @Override
    public Collection<? extends GSIShippingMethodSelectionBucketBO> getShippingMethodSelectionBuckets()
    {
        ShippingMethodSelectionBucketComposer composer = NamingMgr.getManager(ComponentMgr.class).getComponent(
                "shippingMethodSelectionBucketComposer");

        Collection<ShippingMethodSelectionBucketBO> buckets = null;
        Field wrappedField;
        try
        {
            wrappedField = B2CBasketBOImpl.class.getDeclaredField("wrapped");
            wrappedField.setAccessible(true);
          
            buckets = composer.composeShippingBuckets((B2CBasketBO)wrappedField.get(delegate), context);

        }
        catch(NoSuchFieldException e)
        {
            Logger.error(this, " field not found ", e);
        }
        catch(SecurityException e)
        {
            Logger.error(this, " security fault ", e);
        }
        catch(IllegalArgumentException e)
        {
            Logger.error(this, " illegal argument ", e);
        }
        catch(IllegalAccessException e)
        {
            Logger.error(this, " illegal access ", e);
        }
        
        //TODO set shippingMethods
        TreeSet<ShippingMethod> shippingMethods = new TreeSet<ShippingMethod>(new ShippingMethodComparator());
        
        List<GSIShippingMethodSelectionBucketBO> bbos = new ArrayList<GSIShippingMethodSelectionBucketBO>(buckets.size());
        for(ShippingMethodSelectionBucketBO shippingBucketBO : buckets)
        {
            GSIShippingMethodSelectionBucketBO gsiShippingBucketBO = new GSIShippingMethodSelectionBucketBOImpl(shippingBucketBO, this, context, calculationResultView);
            bbos.add(gsiShippingBucketBO);
        }
        return bbos;
    } 
    
    
    @Override
    public Collection<? extends GSIShippingBucketBO> getShippingBuckets()
    {
        ShippingBucketComposer composer = NamingMgr.getManager(ComponentMgr.class).getComponent(
                "shippingBucketComposer");
        
        List<ShippingBucket> buckets = null;
        Field wrappedField;
        try
        {
            wrappedField = LineItemCtnrBOImpl.class.getDeclaredField("wrapped");
            wrappedField.setAccessible(true);
         
            buckets = composer.composeShippingBuckets((LineItemCtnr)wrappedField.get(delegate));
        }
        catch(NoSuchFieldException e)
        {
            Logger.error(this, " field not found ", e);
        }
        catch(SecurityException e)
        {
            Logger.error(this, " security fault ", e);
        }
        catch(IllegalArgumentException e)
        {
            Logger.error(this, " illegal argument ", e);
        }
        catch(IllegalAccessException e)
        {
            Logger.error(this, " illegal access ", e);
        }
        
        List<GSIShippingBucketBO> bbos = new ArrayList<GSIShippingBucketBO>(buckets.size());
        for(ShippingBucket shippingBucket : buckets)
        {
            ShippingBucketBO shippingBucketBO = new ShippingBucketBOImpl(shippingBucket, delegate, context, calculationResultView);
            bbos.add(new GSIShippingBucketBOImpl(shippingBucketBO, this, context, calculationResultView ));
        }
        return bbos;
    }  
    
}